package com.jiguang.property.service.impl.userImpl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.api.R;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jiguang.property.config.LoginUser;
import com.jiguang.property.dto.UserInfoDTO;
import com.jiguang.property.entity.*;
import com.jiguang.property.handler.GuliException;
import com.jiguang.property.mapper.UserMapper;
import com.jiguang.property.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jiguang.property.utils.*;
import io.netty.util.internal.StringUtil;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.parameters.P;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.management.QueryEval;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author testjava
 * @since 2025-01-19
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private OperateLogService operateLogService;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private ParkInfoService parkInfoService;
    @Autowired
    private CostInfoService costInfoService;
    @Autowired
    private UpkeepInfoService upkeepInfoService;
    @Autowired
    private JwtUtils tokenManager;
    @Resource(name = "Normal")
    private RedisTemplate<Object, Object> redisTemplate;
    @Resource
    private RedisCache redisCache;
    @Autowired
    private JavaMailSender mailSender;
    @Autowired
    private HostelInfoService hostelInfoService;

    @Value("${spring.mail.username}")
    private String from;
    @Value("${spring.mail.nickname}")
    private String nickname;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result login(UserInfoDTO user) {
        String falge = user.getUsername();
        //判断用户登录的方式
        if (Objects.isNull(user.getUsername())) {
            falge = user.getEmail();
        }
        //AuthenticationManager进行用户认证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(falge, user.getPassword());
        Authentication authenticate = null;
        try {
            authenticate = authenticationManager.authenticate(authenticationToken);
        } catch (Exception e) {
            //记录登陆日志
            throw new GuliException(11001, "用户名或密码错误");
        }
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        //判断该用户是否已删除
        UserInfoDTO userInfoDTO = loginUser.getUcenter();
        if (userInfoDTO.getIsDelete() == 1){
            throw new GuliException(11001, "用户已禁用，请联系管理员");
        }
        //如果认证通过。使用userid生成一个jwt
        String userId = loginUser.getUcenter().getId();
        String token = tokenManager.createToken(userId);
        //把完整的用户信息存入redis userid作为key
        redisTemplate.opsForValue().set("login:" + userId, loginUser.getUcenter(), 24, TimeUnit.HOURS);
        //将用户信息返回给前端
        UserInfoDTO userInfoDto = loginUser.getUcenter();
        userInfoDto.setPassword("");
        //记录登陆日志
        LoginOperateLog(userInfoDto,"登录成功");
        return Result.ok().message("登录成功").data("token", token).data("userInfo", userInfoDto);
    }

    /**
     * 记录登陆的操作日志
     */
    private void LoginOperateLog(UserInfoDTO userInfoDTO,String operateName){
        OperateLog operateLog = new OperateLog();

        //通过工具类获取Request对象
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();

        //访问的url
        String url = request.getRequestURI().toString();
        operateLog.setUrl(url);
        //请求方式
        String requestMethod = request.getMethod();
        operateLog.setRequestWay(requestMethod);

        //登陆IP
        String ipAddr = IpAddressUtil.getIpAddr(request);
        operateLog.setLoginIp(ipAddr);

        operateLog.setName(operateName);

        operateLog.setUserId(userInfoDTO.getId());

        //保存到数据库
        operateLogService.save(operateLog);
    }

    @Override
    public Result register(UserInfoDTO user) {
        //判断用户名和密码是否为空
        if (StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getEmail()) ||
                StringUtils.isEmpty(user.getPassword()) || StringUtils.isEmpty(user.getAgainPassword())) {
            throw new GuliException(11001, "请完善信息");
        }
        if (StringUtils.isEmpty(user.getCode())){
            throw new GuliException(11001, "请输入验证码");
        }
        //判断用户名的格式是否正确（只能填英文和数子）
        String put = "^[a-z0-9A-Z]+$";
        if (!user.getUsername().matches(put)) {
            throw new GuliException(11001, "用户名格式不正确");
        }
        //判断用户名是否唯一
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("username", user.getUsername());
        User userInfo = baseMapper.selectOne(wrapper);
        if (userInfo != null) {
            throw new GuliException(409, "用户名已存在");
        }
        //判断用户密码和确认密码是否一致
        if (!user.getPassword().equals(user.getAgainPassword())) {
            throw new GuliException(11001, "两次输入不一致");
        }
        //从redis中获取验证码，判断是否一致
        String redisCode = (String) redisTemplate.opsForValue().get(user.getEmail());
        if (StringUtils.isEmpty(redisCode)) {throw new GuliException(11001, "验证码无效，请重新获取");}
        if (!redisCode.equals(user.getCode())) {
            throw new GuliException(11001, "验证码错误");
        }
        //判断邮箱是否唯一
        QueryWrapper wrapperByemail = new QueryWrapper();
        wrapperByemail.eq("email", user.getEmail());
        User userInfoByemail = baseMapper.selectOne(wrapperByemail);
        if (userInfoByemail != null) {
            throw new GuliException(11002, "邮箱已存在");
        }
        //对密码进行加密
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encode = passwordEncoder.encode(user.getPassword());
        //将数据封装入ucenter并存入数据库
        User ucenter = new User();
        ucenter.setPassword(encode);
        ucenter.setUsername(user.getUsername());
        ucenter.setEmail(user.getEmail());
        //设置默认头像
        ucenter.setAvatar("https://med-10.oss-cn-beijing.aliyuncs.com/Snipaste_2023-02-18_20-30-47.png");
        baseMapper.insert(ucenter);
        return Result.ok();
    }

    @Override
    public Result forget(UserInfoDTO user) {

        //判断密码和邮箱是否为空
        if (StringUtils.isEmpty(user.getPassword()) || StringUtils.isEmpty(user.getAgainPassword())
                || StringUtils.isEmpty(user.getEmail())) {
            throw new GuliException(11001, "请完善信息");
        }

        //判断两次输入密码是否一致
        if (!user.getPassword().equals(user.getAgainPassword())) {
            throw new GuliException(11001, "两次密码输入不一致");
        }

        //判断验证码是否正确
        String redisCode = (String) redisTemplate.opsForValue().get(user.getEmail());
        if (!redisCode.equals(user.getCode())) {
            throw new GuliException(11001, "验证码错误");
        }

        //修改密码
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("email", user.getEmail());
        //密码加密
        String encode = new BCryptPasswordEncoder().encode(user.getPassword());
        User ucenter = new User();
        ucenter.setPassword(encode);
        int update = baseMapper.update(ucenter, wrapper);
        if (update >= 1) {
            return Result.ok();
        }
        return Result.error();
    }

    @Override
    public Result getRandom(String email) {
        //创建邮箱信息
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(nickname + '<' + from + '>');
        message.setTo(email);
        message.setSubject("您本次的验证码是：");
        //获取验证码
        String code = RandomUtils.getSixBitRandom();
        message.setText("尊敬的用户，您好：\n" +
                " \n 本次请求的邮箱验证码为：" + code + ",验证码5分钟内有效，请及时输入" +
                " \n如非本人操作，请忽略该邮件。\n(这是一封通过自动发送的邮件，请不要直接回复）");
        mailSender.send(message);
        //发送成功把验证码放到redis中
        redisTemplate.opsForValue().set(email, code, 5, TimeUnit.MINUTES);
        return Result.ok().message("发送成功");
    }

    @Override
    public Result logout(HttpServletRequest request) {
        //获取token从请求头中获取
        String token = request.getHeader("token");
        //解析token获取userid
        String userId = tokenManager.getUserFromToken(token);
        //从redis中获取用户信息
        String redisKey = "login:" + userId;

        //删除redis的值
        redisCache.deleteObject(redisKey);
        return Result.ok().message("退出成功");
    }

    @Override
    public Result updateUserInfo(UserInfoDTO userInfoDTO) {
        if (StringUtil.isNullOrEmpty(userInfoDTO.getId())){
            return Result.error().message("用户唯一标识不能为空");
        }

        if (!StringUtil.isNullOrEmpty(userInfoDTO.getPassword())){
            //密码加密
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            userInfoDTO.setPassword(passwordEncoder.encode(userInfoDTO.getPassword()));
        }else{
            userInfoDTO.setPassword(null);
        }

        User user = new User();
        BeanUtils.copyProperties(userInfoDTO,user);
        //更改缓存信息
        redisTemplate.opsForValue().set("login:" + userInfoDTO.getId(), userInfoDTO, 24, TimeUnit.HOURS);

        //更改用户信息
        baseMapper.updateById(user);
        return Result.ok().message("更改成功");
    }

    @Override
    public Result getUserByPosition(Boolean isAll,UserInfoDTO userInfoDTO,Boolean isPage) {
        //创建分页对象
        Page<User> page = new Page<>(userInfoDTO.getPageNow(),userInfoDTO.getPageSize());

        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("is_delete",0).orderByDesc("update_time");

        if (!StringUtils.isEmpty(userInfoDTO.getUsername())){
            wrapper.like("username",userInfoDTO.getUsername());
        }

        List<User> userList = new ArrayList<>();
        Long total;
        if (isAll){
            if(isPage){
                Page<User> userPage = (Page<User>) baseMapper.selectPage(page,wrapper);
                userList = userPage.getRecords();
                total = userPage.getTotal();
            }else{
                userList = baseMapper.selectList(wrapper);
                total = Long.valueOf(userList.size());
            }
        }else{
            wrapper.eq("position",0).or().eq("position",4);
            if(isPage){
                Page<User> userPage = (Page<User>) baseMapper.selectPage(page,wrapper);
                userList = userPage.getRecords();
                total = userPage.getTotal();
            }else{
                userList = baseMapper.selectList(wrapper);
                total = Long.valueOf(userList.size());
            }
        }

        return Result.ok().data("userList",userList).data("total",total);
    }

    @SneakyThrows
    @Override
    public Result deleteUserInfo(String userIds) {
        if (StringUtils.isEmpty(userIds)) return Result.error().message("Id不能为空");

        Boolean aBoolean = checkAuthority();
        if (aBoolean){
            return Result.error().message("权限不足");
        }

        List<String> ids = Arrays.asList(userIds.split(","));
        //查询删除的用户是否欠费
        //查询删除的用户是否是业主（存在停车位，房屋）
        for (String id : ids) {
            QueryWrapper<ParkInfo> pWrapper = new QueryWrapper<>();
            pWrapper.eq("user_id",id);
            int pCount = parkInfoService.count(pWrapper);
            if (pCount > 0){
                return Result.error().message("该用户存在停车位");
            }

            QueryWrapper<HostelInfo> hWrapper = new QueryWrapper<>();
            hWrapper.eq("user_id",id);
            int hCount = hostelInfoService.count(hWrapper);
            if (hCount > 0){
                return Result.error().message("该用户存在房屋");
            }

            QueryWrapper<CostInfo> cWrapper = new QueryWrapper<>();
            cWrapper.eq("user_id",id);
            cWrapper.eq("status",0);
            int cCount = costInfoService.count(cWrapper);
            if (cCount > 0){
                return Result.error().message("该用户存在欠费");
            }

            QueryWrapper<UpkeepInfo> uWrapper = new QueryWrapper<>();
            uWrapper.and(wrapper ->
                    wrapper.eq("reporter_id", id).or().eq("upkeep_id", id)
            ).and(wrapper ->
                    wrapper.eq("status", 1).or().eq("status", 2)
            );
            int uCount = upkeepInfoService.count(uWrapper);
            if (uCount > 0){
                return Result.error().message("该用户存在维修订单未完成");
            }
        }


        baseMapper.deleteBatchIds(ids);
        return Result.ok().message("删除成功");
    }

    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result saveOrUpdateById(UserInfoDTO userInfoDTO) {
        Boolean aBoolean = checkAuthority();
        if (aBoolean){
            return Result.error().message("权限不足");
        }

        //判断用户名和密码是否为空
        if (StringUtils.isEmpty(userInfoDTO.getUsername()) || StringUtils.isEmpty(userInfoDTO.getEmail())
                || StringUtils.isEmpty(userInfoDTO.getPassword())
        || (StringUtils.isEmpty(userInfoDTO.getHostelIds()) && userInfoDTO.getPosition() == 0)) {
            throw new GuliException(11001, "请完善信息");
        }

        //判断用户名是否唯一
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("username", userInfoDTO.getUsername());
        User userInfo = baseMapper.selectOne(wrapper);
        if (userInfo != null && StringUtils.isEmpty(userInfoDTO.getId())) {
            throw new GuliException(409, "用户名已存在");
        }else if(userInfo != null && !StringUtils.isEmpty(userInfoDTO.getId()) && !userInfo.getId().equals(userInfoDTO.getId())){
            throw new GuliException(409, "用户名已存在");
        }

        //判断邮箱是否唯一
        QueryWrapper wrapperByemail = new QueryWrapper();
        wrapperByemail.eq("email", userInfoDTO.getEmail());
        User userInfoByemail = baseMapper.selectOne(wrapperByemail);
        if (userInfoByemail != null && StringUtils.isEmpty(userInfoDTO.getId())) {
            throw new GuliException(11002, "邮箱已存在");
        }else if(userInfoByemail != null && !StringUtils.isEmpty(userInfoDTO.getId()) && !userInfo.getId().equals(userInfoDTO.getId())){
            throw new GuliException(11002, "邮箱已存在");
        }

        if(StringUtils.isEmpty(userInfoDTO.getId())){
            //对密码进行加密
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            String encode = passwordEncoder.encode(userInfoDTO.getPassword());

            //将数据封装入ucenter并存入数据库
            User ucenter = new User();
            ucenter.setPassword(encode);
            ucenter.setUsername(userInfoDTO.getUsername());
            ucenter.setEmail(userInfoDTO.getEmail());
            ucenter.setSex(userInfoDTO.getSex());
            //设置默认头像
            ucenter.setAvatar("https://med-10.oss-cn-beijing.aliyuncs.com/Snipaste_2023-02-18_20-30-47.png");

            baseMapper.insert(ucenter);
        }else{
            User user = new User();
            BeanUtils.copyProperties(userInfoDTO,user);

            //更改用户信息
            baseMapper.updateById(user);
        }

        //更新用户的房屋信息
        if (userInfoDTO.getPosition() == 0){
            List<String> hostelIdList = Arrays.asList(userInfoDTO.getHostelIds().split(","));


            //获取该用户的房屋信息(记录哪个房屋已经业主不是他的，将其房屋的UserId清空)
            QueryWrapper<HostelInfo> hostelWrapper = new QueryWrapper<>();
            hostelWrapper.eq("user_id",userInfoDTO.getId());
            List<HostelInfo> hostelInfoList = hostelInfoService.list(hostelWrapper);

            if (CollectionUtils.isEmpty(hostelInfoList)){
                updateHostelById(hostelIdList,userInfoDTO.getId(),false);
            }else{
                List<String> delHostelIds = new ArrayList<>();
                List<String> addHostelIds = new ArrayList<>();

                HashSet<String> newHostelIdSet = new HashSet<>();
                hostelIdList.forEach(id -> newHostelIdSet.add(id));

                HashSet<String> oldHostelIdSet = new HashSet<>();
                hostelInfoList.forEach(hostelInfo -> {
                    oldHostelIdSet.add(hostelInfo.getId());
                    if(!newHostelIdSet.contains(hostelInfo.getId())){
                        delHostelIds.add(hostelInfo.getId());
                    }
                });
                hostelIdList.forEach(id -> {
                    if(!oldHostelIdSet.contains(id)){
                        addHostelIds.add(id);
                    }
                });
                //查询是否欠费
                QueryWrapper<CostInfo> cWrapper = new QueryWrapper<>();
                cWrapper.eq("user_id",userInfoDTO.getId());
                cWrapper.eq("status",0);
                int cCount = costInfoService.count(cWrapper);
                if (cCount > 0){
                    throw new GuliException(11001, "该用户存在欠费");
                }

                QueryWrapper<UpkeepInfo> uWrapper = new QueryWrapper<>();
                uWrapper.and(wrapper1 ->
                        wrapper1.eq("reporter_id", userInfoDTO.getId()).or().eq("upkeep_id", userInfoDTO.getId())
                ).and(wrapper1 ->
                        wrapper1.eq("status", 1).or().eq("status", 2)
                );
                int uCount = upkeepInfoService.count(uWrapper);
                if (uCount > 0){
                    throw new GuliException(11001, "该用户存在维修订单未完成");
                }


                //删除的用户房屋
                updateHostelById(delHostelIds,userInfoDTO.getId(),true);
                //新增用户的房屋
                updateHostelById(addHostelIds,userInfoDTO.getId(),false);
            }

        }else{
            //如果不是业主，清空用户的房屋

            //查询是否欠费
            QueryWrapper<CostInfo> cWrapper = new QueryWrapper<>();
            cWrapper.eq("user_id",userInfoDTO.getId());
            cWrapper.eq("status",0);
            int cCount = costInfoService.count(cWrapper);
            if (cCount > 0){
                throw new GuliException(11001, "该用户存在欠费");
            }

            QueryWrapper<UpkeepInfo> uWrapper = new QueryWrapper<>();
            uWrapper.and(wrapper1 ->
                    wrapper1.eq("reporter_id", userInfoDTO.getId()).or().eq("upkeep_id", userInfoDTO.getId())
            ).and(wrapper1 ->
                    wrapper1.eq("status", 1).or().eq("status", 2)
            );
            int uCount = upkeepInfoService.count(uWrapper);
            if (uCount > 0){
                throw new GuliException(11001, "该用户存在维修订单未完成");
            }

            QueryWrapper<HostelInfo> hostelWrapper = new QueryWrapper<>();
            hostelWrapper.eq("user_id",userInfoDTO.getId());
            List<HostelInfo> hostelInfoList = hostelInfoService.list(hostelWrapper);

            if (!CollectionUtils.isEmpty(hostelInfoList)){
                List<String> hostelIdList = new ArrayList<>();
                hostelInfoList.forEach(hostelInfo -> {hostelIdList.add(hostelInfo.getId());});
                //删除的用户房屋
                updateHostelById(hostelIdList,userInfoDTO.getId(),true);
            }
        }


        return Result.ok();
    }

    /**
     * 修改房屋信息
     */
    private void updateHostelById(List<String> hostelIds, String userId, Boolean isUserId){
        HostelInfo hostelInfo = new HostelInfo();
        hostelInfo.setUserId(userId);
        hostelIds.forEach(id -> {
            hostelInfo.setId(id);
            if (isUserId){
                hostelInfo.setUserId("");
                hostelInfo.setStatus(0);
            }else{
                hostelInfo.setStatus(1);
            }
            hostelInfoService.updateById(hostelInfo);
        });
    }

    /**
     * 权限校验
     */
    private Boolean checkAuthority() throws JsonProcessingException {
        //通过工具类获取Request对象
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();
        //获取用户名
        //需要通过解析token获取
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)){
            throw new GuliException(403,"登陆过期");
        }
        String userId = tokenManager.getUserFromToken(token);

        //从redis中获取用户信息
        String redisKey = "login:" + userId;
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        String cacheObject = (String) redisTemplate.opsForValue().get(redisKey);

        if (StringUtils.isEmpty(cacheObject)) {
            throw new GuliException(403,"登陆过期");
        }

        UserInfoDTO ucenter = new ObjectMapper().readValue(cacheObject.toString(), UserInfoDTO.class);

        if (ucenter.getPosition() != 2){
            return true;
        }
        return false;
    }
}
